using System;

using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;

namespace Org.BouncyCastle.Asn1.X9
{
	/**
	 * ASN.1 def for Elliptic-Curve ECParameters structure. See
	 * X9.62, for further details.
	 */
	public class X9ECParameters
		: Asn1Encodable
	{
		private X9FieldID	fieldID;
		private ECCurve		curve;
		private ECPoint		g;
		private BigInteger	n;
		private BigInteger	h;
		private byte[]		seed;

		public X9ECParameters(
			Asn1Sequence seq)
		{
			if (!(seq[0] is DerInteger)
			   || !((DerInteger) seq[0]).Value.Equals(BigInteger.One))
			{
				throw new ArgumentException("bad version in X9ECParameters");
			}

			X9Curve x9c = null;
			if (seq[2] is X9Curve)
			{
				x9c = (X9Curve) seq[2];
			}
			else
			{
				x9c = new X9Curve(
					new X9FieldID(
						(Asn1Sequence) seq[1]),
						(Asn1Sequence) seq[2]);
			}

			this.curve = x9c.Curve;

			if (seq[3] is X9ECPoint)
			{
				this.g = ((X9ECPoint) seq[3]).Point;
			}
			else
			{
				this.g = new X9ECPoint(curve, (Asn1OctetString) seq[3]).Point;
			}

			this.n = ((DerInteger) seq[4]).Value;
			this.seed = x9c.GetSeed();

			if (seq.Count == 6)
			{
				this.h = ((DerInteger) seq[5]).Value;
			}
		}

		public X9ECParameters(
			ECCurve		curve,
			ECPoint		g,
			BigInteger	n)
			: this(curve, g, n, BigInteger.One, null)
		{
		}

		public X9ECParameters(
			ECCurve		curve,
			ECPoint		g,
			BigInteger	n,
			BigInteger	h)
			: this(curve, g, n, h, null)
		{
		}

		public X9ECParameters(
			ECCurve		curve,
			ECPoint		g,
			BigInteger	n,
			BigInteger	h,
			byte[]		seed)
		{
			this.curve = curve;
			this.g = g;
			this.n = n;
			this.h = h;
			this.seed = seed;

			if (curve is FpCurve)
			{
				this.fieldID = new X9FieldID(((FpCurve) curve).Q);
			}
			else if (curve is F2mCurve)
			{
				F2mCurve curveF2m = (F2mCurve) curve;
				this.fieldID = new X9FieldID(curveF2m.M, curveF2m.K1,
					curveF2m.K2, curveF2m.K3);
			}
		}

		public ECCurve Curve
		{
			get { return curve; }
		}

		public ECPoint G
		{
			get { return g; }
		}

		public BigInteger N
		{
			get { return n; }
		}

		public BigInteger H
		{
			get
			{
				if (h == null)
				{
					// TODO - this should be calculated, it will cause issues with custom curves.
					return BigInteger.One;
				}

				return h;
			}
		}

		public byte[] GetSeed()
		{
			return seed;
		}

		/**
		 * Produce an object suitable for an Asn1OutputStream.
		 * <pre>
		 *  ECParameters ::= Sequence {
		 *      version         Integer { ecpVer1(1) } (ecpVer1),
		 *      fieldID         FieldID {{FieldTypes}},
		 *      curve           X9Curve,
		 *      base            X9ECPoint,
		 *      order           Integer,
		 *      cofactor        Integer OPTIONAL
		 *  }
		 * </pre>
		 */
		public override Asn1Object ToAsn1Object()
		{
			Asn1EncodableVector v = new Asn1EncodableVector(
				new DerInteger(1),
				fieldID,
				new X9Curve(curve, seed),
				new X9ECPoint(g),
				new DerInteger(n));

			if (h != null)
			{
				v.Add(new DerInteger(h));
			}

			return new DerSequence(v);
		}
	}
}
